package org.devastra.arcinspect.plan;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.devastra.arcinspect.conf.ArcConstants;
import org.devastra.arcinspect.plan.data.GridLocation;
import org.devastra.arcinspect.plan.data.Layer;
import org.devastra.arcinspect.plan.data.Module;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class ArchitecturePlan {

	private File arcPlanFile;
	private Document arcPlanDoc;
	private Map<String, Layer> layerId2ArcLayerMap;
	private Map<Integer, Layer> posArc2LayerMap;
	private Map<String, Module> idArc2ModuleMap;
	private Map<Integer, Module> posArc2ModuleMap;

	private Map<String, GridLocation> packageName2GridLocMap;
	
	public ArchitecturePlan() {
		layerId2ArcLayerMap = new HashMap<String, Layer>();
		idArc2ModuleMap = new HashMap<String, Module>();
		posArc2LayerMap = new HashMap<Integer, Layer>();
		posArc2ModuleMap = new HashMap<Integer, Module>();
		packageName2GridLocMap = new HashMap<String, GridLocation>();
	}
	
	public static ArchitecturePlan getArchitecturePlan(File _file) {
		
		ArchitecturePlan plan = new ArchitecturePlan();
		if(!_file.exists() || !_file.isDirectory()) {
			
			plan.arcPlanFile = _file;
			
			try {
				SAXBuilder builder = new SAXBuilder();
				plan.arcPlanDoc = builder.build(plan.arcPlanFile);
				plan.initialize();
			} catch (JDOMException e) {
				e.printStackTrace();
				plan = null;
			} catch (IOException e) {
				e.printStackTrace();
				plan = null;
			}
			
		}
		
		return plan;
	}
	
	public int getPackageLocation(String _packageName) {
		int loc = -1;
		GridLocation gloc = packageName2GridLocMap.get(_packageName);
		if(gloc != null) {
			loc = gloc.getAggPosition();
		}
		return loc;
	}
	
	@SuppressWarnings("unchecked")
	private void initialize() {
		
		Element root = arcPlanDoc.getRootElement();
		
		//read the layers
		List<Element> layerList= root.getChild("ArcLayers", ArcConstants.ARC_NAMESPACE_URI).getChildren("Layer", ArcConstants.ARC_NAMESPACE_URI);
		System.out.println("Number of Layers defined: "+layerList.size());
		
		for(Element layerElem : layerList) {
			Layer layer = Layer.getLayer(layerElem);
			if(validateLayer(layer)) {
				registerLayer(layer);
			} else {
				System.out.println("Cannot get a valid layer from Element : "+layerElem.toString());
			}
			
			//debug
			System.out.println("Layer = "+layer.toString());
		}
		
		//read the modules
		List<Element> moduleList = root.getChild("ArcModules", ArcConstants.ARC_NAMESPACE_URI).getChildren("Module", ArcConstants.ARC_NAMESPACE_URI);
		System.out.println("Number of Modules defined: "+moduleList.size());
		
		for(Element moduleElem : moduleList) {
			Module module = Module.getModule(moduleElem);
			
			//debug
			System.out.println("Module = "+module.toString());

			if(validateModule(module)) {
				registerModule(module);
				updateModulePackageDetails(module, moduleElem);
			} else {
				System.out.println("Cannot get a valid module from Element : "+moduleElem.toString());
			}
		}
		
	}
	
	private boolean validateLayer(Layer _layer) {
		boolean retVal = true;
		if(_layer == null) {
			retVal = false;
		} else {
			//check layer is unique
			Layer existingLayer = layerId2ArcLayerMap.get(_layer.getId());
			if(existingLayer != null) {
				System.out.println("Duplicate layers found id: "+_layer.getId());
				retVal = false;
			} 
			
			existingLayer = posArc2LayerMap.get(_layer.getPosition());
			if(existingLayer != null) {
				System.out.println("Two layers defined for same position ["+_layer.getPosition()
						+"] Layer[1]: "+_layer.getId()
						+" Layer[2]:"+existingLayer.getId());
				retVal = false;
			} 
		}
		return retVal;
	}
	
	private void registerLayer(Layer _layer) {
		layerId2ArcLayerMap.put(_layer.getId(), _layer);
		posArc2LayerMap.put(_layer.getPosition(), _layer);
	}

	private boolean validateModule(Module _module) {
		boolean retVal = true;
		if(_module == null) {
			retVal = false;
		} else {
			//check layer is unique
			Module existingModule = idArc2ModuleMap.get(_module.getId());
			if(existingModule != null) {
				System.out.println("Duplicate modules found id: "+_module.getId());
				retVal = false;
			}

			existingModule = posArc2ModuleMap.get(_module.getPosition());
			if(existingModule != null) {
				System.out.println("Two modules defined for same position ["+_module.getPosition()
						+"] Layer[1]: "+_module.getId()
						+" Layer[2]:"+existingModule.getId());
				retVal = false;
			}
		}
		return retVal;
	}
	
	private void registerModule(Module _module) {
		idArc2ModuleMap.put(_module.getId(), _module);
		posArc2ModuleMap.put(_module.getPosition(), _module);
	}
	
	@SuppressWarnings("unchecked")
	private void updateModulePackageDetails(Module _module, Element _elem) {
		//layerId2PackageListMap.clear();
		
		List<Element> modLayers = _elem.getChildren("ModLayer", ArcConstants.ARC_NAMESPACE_URI);
		for(Element modLayer : modLayers) {
			String layerId = modLayer.getAttributeValue("id");
			
			List<Element> packages = modLayer.getChildren("Package", ArcConstants.ARC_NAMESPACE_URI);
			if(packages.size() > 0) {
				//List<String> packageNameList = new ArrayList<String>();
				for(Element packageElem : packages) {
					String packageName = packageElem.getTextTrim();
					
					//check the package info is unique
					GridLocation gridLoc = packageName2GridLocMap.get(packageName);
					if(gridLoc == null) { //duplicate declaration
						gridLoc = new GridLocation(_module, this.layerId2ArcLayerMap.get(layerId));
						packageName2GridLocMap.put(packageName, gridLoc);
					} else {
						System.out.println("Error: Duplicate GridLocation for package: "+packageName);
					}
					_module.addPackage(layerId, packageName);
				}
			}
		}
		
	}
	
	//test 
	public static void main(String[] args) {
		String fileName = "/home/ashutosh/projects/sample_data/arcinspect/sample_proj_def.xml";
		File defFile = new File(fileName);
		
		ArchitecturePlan plan = ArchitecturePlan.getArchitecturePlan(defFile);
		
		System.out.println("Plan Created:"+plan);
		
		System.out.println("ArcPlan Detail:");
		for(String pacName : plan.packageName2GridLocMap.keySet()) {
			GridLocation gloc = plan.packageName2GridLocMap.get(pacName);
			if(gloc == null) {
				System.out.println("\t No Grid location found for: "+pacName);
			} else {
				System.out.println("\t"+pacName+" = "+gloc.getAggPosition());
			}
		}
	}
}
